tools/utils/src/modules/array-[object-object].js

/**
* @module @svizzle/utils/array-[object-object]
*/

import * as _ from 'lamb';

/**
 * Return a function that expects an object and applies the provided sequence of transforms to the values of the correspondent paths to the input object.
 * Note that transforms to the same path can be repeated.
 *
 * @function
 * @arg {array} pathFnPairs - pairs [path, function]
 * @return {function} - Object -> Object
 *
 * @example
> obj = {
	a: {
		a1: 'a1',
		a2: {
			a21: 'a21',
			a22: '9',
		},
		a3: '3px',
		a4: '2',
	},
	b: {
		b1: 'b1',
		b2: {
			b21: 'foo',
			b22: '9',
			b23: '2',
			b24: '24px'
		},
		b3: '2',
		b4: '4px'
	},
}
> // orthogonal transforms
> transform = applyTransformsSequence([
	['a.a2.a22', _.pipe([Number, Math.sqrt])],
	['a.a3', parseInt],
	['b.b2.b24', parseInt],
	['b.b4', parseInt],
])
> transform(obj)
{
	a: {
		a1: 'a1',
		a2: {
			a21: 'a21',
			a22: 3,
		},
		a3: 3,
		a4: '2'
	},
	b: {
		b1: 'b1',
		b2: {
			b21: 'foo',
			b22: '9',
			b23: '2',
			b24: 24
		},
		b3: '2',
		b4: 4
	},
}

> // modifying modified paths
> transform = applyTransformsSequence([
	['b', _.values],
	['b.1', _.values],
])
> transform(obj)
{
	a: {
		a1: 'a1',
		a2: {
			a21: 'a21',
			a22: '9',
		},
		a3: '3px',
		a4: '2',
	},
	b: [
		'b1',
		[
			'foo',
			'9',
			'2',
			'24px',
		],
		'2',
		'4px'
	],
}

> // modifying paths multiple times
> transform = applyTransformsSequence([
	['b', _.values],
	['b.1', _.values],
	['b', _.flatten],
])
> transform(obj)
{
	a: {
		a1: 'a1',
		a2: {
			a21: 'a21',
			a22: '9',
		},
		a3: '3px',
		a4: '2',
	},
	b: [
		'b1',
		'foo',
		'9',
		'2',
		'24px',
		'2',
		'4px'
	],
}
 * @since 0.6.0
 * @see {@link module:@svizzle/utils/object-[any-object].applyFnMap|applyFnMap}
 * @see {@link module:@svizzle/utils/object-[object-object].makeMergeAppliedFnMap|makeMergeAppliedFnMap}
 * @see {@link module:@svizzle/utils/object-[object-object].transformValues|transformValues}
 * @see {@link module:@svizzle/utils/object-[object-object].transformPaths|transformPaths}
 */
export const applyTransformsSequence = pathFnPairs => obj =>
	_.reduce(pathFnPairs, (acc, [path, fn]) => {
		const value = _.getPathIn(acc, path);

		return _.setPathIn(acc, path, _.application(fn, [value]))
	}, _.merge({}, obj));

/**
 * Return a function plucking the provided keys from the expected object values
 *
 * @function
 * @arg {array} keys - array of keys to pluck
 * @return {function} - Object -> Object
 *
 * @example
> let select = pluckValuesKeys(['a', 'k'])
> select({
	foo: {a: 1, b: 2, c: 3, k: 4},
	bar: {a: 5, b: 8}
})
{
	foo: {a: 1, k: 4},
	bar: {a: 5}
}
 *
 * @since 0.8.0
 * @see https://ascartabelli.github.io/lamb/module-lamb.html#pluck
 */
export const pluckValuesKeys = keys => _.mapValuesWith(_.pick(keys));

/**
 * Return a function expecting an object and returning an object having
 * keys and values defined by applying the provided array of two functions
 * to the input object keys and values.
 *
 * @function
 * @arg {array} - [keysFn, valuesFn] both being (Any -> Any)
 * @return {function} - (Object -> Object)
 *
 * @example
> remap = remapWith([
	key => `${key}${key}`,
	value => 3 * value,
]);
> remap({a: 1, b: 2})
{aa: 3, bb: 6}
 *
 * @since 0.13.0
 */
export const remapWith = ([keysFn, valuesFn]) => _.pipe([
	_.pairs,
	_.mapWith(([key, value]) => [keysFn(key), valuesFn(value)]),
	_.fromPairs,
]);